package util;

import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.regex.Pattern;

import static java.util.regex.Pattern.compile;

/**
 * @author dqy
 */
public class EmailUtils {

    public static final int CHECK_RESULT_TRUE = 1;//表示验证成功
    public static final int CHECK_RESULT_FALSE = 2;//表示验证失败
    public static final int CHECK_RESULT_INVALID = 3;//表示验证码失效，需要重新发送
    private final static Logger logger = LogManager.getLogger(EmailUtils.class);
    private static final String SESSION_KEY_EMAIL_CODE = "sessionKeyEmailCode";//表示邮件验证码
    private static final String SESSION_KEY_EMAIL_TO = "sessionKeyEmailTo";//表示邮件收件人
    private static final String SESSION_KEY_EMAIL_TIME = "sessionKeyEmailTime";//表示邮件发送时间
    private static final String SESSION_KEY_EMAIL_REMAIN_CHECK_COUNT = "sessionKeyEmailRemainCheckCount";//表示剩余验证次数

    private EmailUtils() {
    }

    /**
     * Check email code int.
     *
     * @param request     the request
     * @param response    the response
     * @param email       the email
     * @param requestCode the request code
     * @return 返回1表示验证成功 ；返回2表示验证失败，但还可以继续验证；返回3表示验证失败并且不能继续验证，需要重新发送邮件验证码
     */
    public static int checkEmailCode(HttpServletRequest request, HttpServletResponse response, String email, String requestCode) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null || cookies.length < 4) {
            return CHECK_RESULT_FALSE;
        }
        Cookie toCookie = null;
        Cookie sendTimeCookie = null;
        Cookie codeCookie = null;
        Cookie remainCountCookie = null;
        for (Cookie cookie : cookies) {
            String name = cookie.getName();
            if (SESSION_KEY_EMAIL_TO.equals(name)) {
                toCookie = cookie;
            } else if (SESSION_KEY_EMAIL_TIME.equals(name)) {
                sendTimeCookie = cookie;
            } else if (SESSION_KEY_EMAIL_CODE.equals(name)) {
                codeCookie = cookie;
            } else if (SESSION_KEY_EMAIL_REMAIN_CHECK_COUNT.equals(name)) {
                remainCountCookie = cookie;
            }
        }
        if (toCookie == null || sendTimeCookie == null || codeCookie == null || remainCountCookie == null) {
            return CHECK_RESULT_FALSE;
        }

        if (email == null || requestCode == null) {
            logger.debug("检查邮件验证码失败，email：{}，requestCode：{}", email, requestCode);
            return CHECK_RESULT_FALSE;
        }
        String emailVal = toCookie.getValue();
        if (!email.equals(emailVal)) {
            logger.debug("检查邮件验证码失败，用户填写的邮箱和验证码发送的邮箱不一致，email: {}，requestCode: {}，emailVal:{}", email, requestCode, emailVal);
            return CHECK_RESULT_FALSE;
        }
        Long sendTime = Long.parseLong(sendTimeCookie.getValue());
        //验证码过期失效
        if (sendTime - System.currentTimeMillis() > 1000 * 60 * 30) {
            logger.debug("检查邮件验证码失败，验证码过期，email:{}，requestCode:{}", email, requestCode);
            return CHECK_RESULT_INVALID;
        }

        //如果验证码不匹配
        String sessionCode = codeCookie.getValue();
        Integer remainCount = Integer.parseInt(remainCountCookie.getValue());

        if (!requestCode.equalsIgnoreCase(sessionCode)) {

            remainCount--;
            //没有剩余验证次数了
            if (remainCount <= 0) {
                codeCookie.setMaxAge(0);
                remainCountCookie.setMaxAge(0);
                sendTimeCookie.setMaxAge(0);
                toCookie.setMaxAge(0);
                logger.debug("检查邮件验证码失败，验证码错误，email：{}，requestCode：{}，sessionCode：{}，remainCount：{}", email, requestCode, sessionCode, remainCount);
                setCookies(response, codeCookie, toCookie, sendTimeCookie, remainCountCookie);
                return CHECK_RESULT_INVALID;
            } else {
                remainCountCookie = new Cookie(SESSION_KEY_EMAIL_REMAIN_CHECK_COUNT, String.valueOf(remainCount));
                logger.debug("检查邮件验证码失败，验证码错误，email：{}，requestCode：{}，sessionCode：{}，remainCount：{}", email, requestCode, sessionCode, remainCount);
                setCookies(response, codeCookie, toCookie, sendTimeCookie, remainCountCookie);
                return CHECK_RESULT_FALSE;
            }
        }

        //验证码匹配
        codeCookie.setMaxAge(0);
        remainCountCookie.setMaxAge(0);
        sendTimeCookie.setMaxAge(0);
        toCookie.setMaxAge(0);
        setCookies(response, codeCookie, toCookie, sendTimeCookie, remainCountCookie);
        logger.debug("检查邮件验证码成功，email：{}，requestCode：{}，sessionCode：{}，remainCount：{}", email, requestCode, sessionCode, remainCount);
        return CHECK_RESULT_TRUE;
    }

    //发送邮件验证码
    public static void sendEmailCode(HttpServletRequest request, HttpServletResponse response, String smtpServer, String username, String password, String from, String to) {
        String subject = "邮箱验证码 - 郑在行";
        // 得到一个随机的5位数字的验证码
        String emailCode = new Random().nextInt(90000) + 10000 + "";
        String content = "您的邮箱验证码是：" + emailCode + "（30分钟内有效）";
        try {
            sendEmail(smtpServer, username, password, from, to, subject, content);
            Cookie codeCookie = new Cookie(SESSION_KEY_EMAIL_CODE, emailCode);
            Cookie toCookie = new Cookie(SESSION_KEY_EMAIL_TO, to);
            Cookie timeCookie = new Cookie(SESSION_KEY_EMAIL_TIME, String.valueOf(System.currentTimeMillis()));
            Cookie countCookie = new Cookie(SESSION_KEY_EMAIL_REMAIN_CHECK_COUNT, String.valueOf(5));
            setCookies(response, codeCookie, toCookie, timeCookie, countCookie);
            logger.debug("发送邮件验证码成功，email：{}，code：{}", to, emailCode);
        } catch (Exception e) {
            logger.debug("发送邮件验证码失败，email：{}，code：{}, reason: {}", to, emailCode, e);
            throw new RuntimeException("发送邮箱验证码失败", e);
        }
    }

    public static void sendEmail(String smtpServer, String username, String password, String from, String to, String subject, String content) throws EmailException {
        HtmlEmail email = new HtmlEmail();//发送html格式邮件
        email.setHostName(smtpServer);
        email.setCharset("UTF-8");
        email.setAuthentication(username, password);
        email.setFrom(from);
        email.addTo(to);
        email.setSubject(subject);
        email.setMsg(content);
        email.send();
    }

    //使用sendcloud发送验证码邮件
    public static void sendEmailCodeWithSendCloud(HttpServletRequest request, HttpServletResponse response, String url, String apiUser, String apiKey, String from, String to, String templateInvokeName) {

        // 得到一个随机的5位数字的验证码
        String emailCode = new Random().nextInt(90000) + 10000 + "";

        Map<String, String> params = new HashMap<String, String>();
        params.put("apiUser", apiUser);
        params.put("apiKey", apiKey);
        params.put("from", from);
        params.put("templateInvokeName", templateInvokeName);

        Map<String, Object> xsmtpapiMap = new HashMap<String, Object>();
        xsmtpapiMap.put("to", new String[]{to});

        Map<String, Object> subMap = new HashMap<String, Object>();
        subMap.put("%code%", new String[]{emailCode});

        xsmtpapiMap.put("sub", subMap);

        params.put("xsmtpapi", JsonUtils.toJson(xsmtpapiMap));

        try {

            String result = HttpUtils.post(url, params);
            Pattern pattern = compile("\"result\" *: *true");
            if (!pattern.matcher(result).find()) {
                throw new RuntimeException("发送邮件失败，响应信息为：" + result);
            }
            Cookie codeCookie = new Cookie(SESSION_KEY_EMAIL_CODE, emailCode);
            Cookie toCookie = new Cookie(SESSION_KEY_EMAIL_TO, to);
            Cookie timeCookie = new Cookie(SESSION_KEY_EMAIL_TIME, String.valueOf(System.currentTimeMillis()));
            Cookie countCookie = new Cookie(SESSION_KEY_EMAIL_REMAIN_CHECK_COUNT, String.valueOf(5));
            setCookies(response, codeCookie, toCookie, timeCookie, countCookie);
            logger.debug("发送邮件验证码成功，email：{}，code：{}", to, emailCode);
        } catch (Exception e) {
            logger.debug("发送邮件验证码失败，email：{}，code：{}, reason: {}", to, emailCode, e);
            throw new RuntimeException("发送邮箱验证码失败", e);
        }
    }

    private static void setCookies(HttpServletResponse response, Cookie... cookies) {
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                response.addCookie(cookie);
            }
        }
    }
}